import 'intersection-observer'
import Vue from 'vue'
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'
import './index.scss'

/**
 * How to use
 * <el-table height="100px" v-el-height-adaptive-table="{bottomOffset: 88, minHeight: 400, body: refs.body}">...</el-table>
 * <BaseTable v-el-height-adaptive-table="{bottomOffset: 88, minHeight: 400}"><el-table height="100px" >...</el-table></BaseTable>
 * el-table height is must be set
 * bottomOffset: 88(default)    // The height of the table from the bottom of the page.
 * minHeight: 200(default)
 */
const observeMap = new Map()

const doResize = (el, binding, vnode, $table) => {
  // 当表格不显示在视图中阻止自适应，防止计算错误
  if (!observeMap.get(el).isIntersecting) {
    return
  }
  const { value } = binding

  if (!$table) return
  if (!$table.height) {
    throw new Error(`el-$table must set the height. Such as height='100px', 必须设置height不然无法触发prop相应`)
  }

  const bottomOffset = (value && value.bottomOffset) || 88
  const minHeight = (value && value.minHeight) || 200

  let height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset

  // 防止过度处理，给表格添加一个最小高度
  height = height > minHeight ? height : minHeight
  $table.layout.setHeight(height)
  $table.doLayout()
}

// 优化方案，可以绑定到el-table的父组件，会向下查找el-table组件进行下一步处理
function findTableComponentInstance(componentInstance) {
  if (componentInstance.$vnode.tag.indexOf('ElTable') >= 0) {
    return componentInstance
  }
  let target = null
  for (const item of (componentInstance.$children || [])) {
    target = findTableComponentInstance(item)
    if (target) {
      return target
    }
  }
}

function bindListener(context, binding, vnode) {
  const { componentInstance } = vnode
  let { body } = binding
  body = body || window.document.body
  const $table = findTableComponentInstance(componentInstance)
  const el = $table.$el

  // 当table出现在视图窗口时候，重新计算高度
  const observer = new IntersectionObserver(
    (entries) => {
      for (const entry of entries) {
        if (entry.isIntersecting) {
          observeMap.set(el, {
            isIntersecting: true,
            observer
          })
          context.className += ' v-adaptive'

          doResize(el, binding, vnode, $table)
          Vue.nextTick(() => {
            context.className = context.className.replaceAll('v-adaptive', '')
          })
        } else {
          observeMap.set(el, {
            isIntersecting: false,
            observer
          })
          context.className += ' v-adaptive'
        }
      }
    }
  )

  observer.observe(el)

  el.resizeListener = () => {
    doResize(el, binding, vnode, $table)
  }

  // parameter 1 is must be "Element" type
  addResizeListener(body, el.resizeListener)
}

export default {
  bind(context, binding, vnode) {
    context.className += ' v-adaptive'
    const { body } = binding
    if (!body) {
      bindListener(context, binding, vnode)
    }
  },
  inserted(context, binding, vnode) {
    const { body } = binding
    if (body) {
      bindListener(context, binding, vnode)
    }
  },
  unbind(_, binding, vnode) {
    const { componentInstance } = vnode
    let { body } = binding
    body = body || window.document.body
    const $table = findTableComponentInstance(componentInstance)
    const el = $table.$el
    observeMap.get(el).observer.disconnect()
    observeMap.delete(el)
    removeResizeListener(body, el.resizeListener)
  }
}
